perm filename IPSER.MAC[IP,NET] blob sn#702346 filedate 1983-03-09 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00032 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00004 00002		title	IPSer
C00005 00003	\
C00006 00004		subttl	defintions describing an IP leader
C00009 00005		subttl	other definitions for IP header
C00011 00006		subttl	FDB - fragmentation data block
C00016 00007		subttl	IpIn - handle an incoming IP message
C00020 00008		subttl	now parse options
C00022 00009		subttl	rebuild a fragmented message
C00033 00010		subttl	returns
C00035 00011		subttl	Option handlers
C00037 00012		subttl	Option parsing code
C00043 00013		subttl	subroutines to help option parsing	  
C00046 00014		subttl	protocol definitions
C00048 00015		subttl	AllFDB
C00050 00016		subttl	FlsFDB
C00052 00017		subttl	IpSec
C00057 00018		subttl	NxtOBf
C00060 00019		subttl	IPMake
C00065 00020		subttl	GetID
C00066 00021		subttl	ICMP message handling code
C00070 00022		subttl	ICMPIn - handle an incoming ICMP message
C00073 00023		subttl	ICMP returns
C00074 00024		subttl	ICMP message type definitions
C00076 00025		subttl	handlers for different ICMP message types
C00081 00026		subttl	ICMDDB
C00084 00027		subttl	ICMPMk
C00088 00028		subttl	RedSnd
C00091 00029		subttl	CutSnd
C00095 00030		subttl	SndNSP
C00097 00031		subttl	data storage area
C00098 00032		$lit
C00099 ENDMK
C⊗;
	title	IPSer
	subttl	provan

	search	f,s
	search	NetDef		; get network definitions
	search	MacTen		; search *after* NetDef

	sall

	$reloc
	$high


XP	VIPSer,1		; first IP version
comment	\

this module contains the support routines for the internet protocol
as defined in RFC-791 and support for its companion protocol, the
internet control message protocol as defined in RFC-792.

\
	subttl	defintions describing an IP leader

; see RFC-791 for details of this header.

.IpVer==4		; version of IP that this module understands
.IpTTL==MSL		; make MSL the time to live for our packets (1 min.)
MaxIPS==↑d576		; recommended maximum size of IP message

IpLen==:5		; number of words in an IP leader (not
			;  including options).

	$low		; define the storage needed

IpIBHd:	block	NBHLen			; header in case ICMP has to send this
					;  leader back out.
IpIBuf:	block	IpLen			; words needed for header

; the following block is used and removed under ScnOff.
IpOBuf:	block	NBHLen+IpLen		; buffer for forming IP leaders
					;  for output.

	$high		; back to protected code

IpPnt:	point	8,IpIBuf		; pointer to start loading the
					;  header block from the stream.

; define the actual header fields.  position is the bit position of the
;  left most bit.
;
; 	name   word  position width
DefFd.	IPVers,	0,	0,	4	; version of this message's protocol
DefFd.	IpIHL,	0,	4,	4	; internet header length (32 bit words)
DefFd.	IPTOS,	0,	8,	8	; type of service
DefFd.	IPTLen,	0,	16,	16	; total length (8 bit bytes)
DefFd.	IpId,	1,	0,	16	; identification
DefFd.	IpFrgF,	1,	16,	16	; entire fragment field
	IP%MF==<1←↑d13>		; more fragments after this one if set.
DefFd.	IpFOff,	1,	19,	13	; fragment offset (8 octets)
DefFd.	IpTTL,	2,	0,	8	; time to live
DefFd.	IpProt,	2,	8,	8	; protocol of next level
DefFd.	IpHChk,	2,	16,	16	; header checksum
DefFd.	IpSA,	3,	0,	32	; source address
DefFd.	IpDA,	4,	0,	32	; destination address
	subttl	other definitions for IP header

; flags in flag field of the time-stamp IP option.  these bits are
;  not explicitly defined in the protocol, but they agree with the
;  definitions for this field after we've made sure not to try to
;  handle values in this field we do not understand.  these flags
;  are kept in the right half of S while processing a time-stamp option.

	IP%Adr==1			; each timestamp is preceeded
					;  by an internet address.
	IP%Set==2			; the internet addresses in the
					;  option are preset: a host
					;  should only fill in the
					;  time-stamp field if it finds
					;  its host number in the
					;  internet address field.

; flags found in left half of S put in the left half of S to spot various
;  conditions which are detected during option processing.

	IP$Str==1b17			; strict routing was indicated.
					;  targetting MUST be directly
					;  to the next host in the route
					;  (next host is already loaded
					;  into IPDA.)
	IP$Rou==1b16			; used internally in option
					;  parsing to distinguich
					;  between a source route and a
					;  record route option.


IPAddr::	ArpAdr!ThSite##		; get our site number
	subttl	FDB - fragmentation data block

;++
;
;	block containing data to allow a fragmented message to
;	be reassembled.
;
;--


;;!------------------------------------|------------------------------------!
;;!	    last FDB in chain	       |	next FDB in chain	    !
;;!------------------------------------|------------------------------------!
;;!			source address of this fragment			    !
;;!------------------------------------|------------------------------------!
;;!			destination address of this fragment		    !
;;!------------------------------------|------------------------------------!
;;!	protocol of this fragment      |     message ID of this message     !
;;!------------------------------------|------------------------------------!
;;!		bit mask: one bit for each 8 byte group, set if		    !
;;!		this group has arrived.  group 0 is represented		    !
;;!		by low order bit of the first word.  the number		    !
;;!		of words in the mask depends on the maximum message	    !
;;!		size we are prepared to accept.				    !
;;!------------------------------------|------------------------------------!
;;!		total length of message in bytes, if known		    !
;;!------------------------------------|------------------------------------!
;;!			count of octoctets currently in buffers		    !
;;!------------------------------------|------------------------------------!
;;!	 	    trash	       |	pointer to first buffer     !
;;!------------------------------------|------------------------------------!
;;!			time to live for this FDB			    !
;;!------------------------------------|------------------------------------!

bkini.		; initialize allocation mechanism

BkNxt.	FDBLst,hlf.wd		;(LH) previous FDB in chain
BkNxt.	FDBNxt,hlf.wd		;(RH) next FDB in chain

BkNxt.	FDBSou			; source address
BkNxt.	FDBDes			; destination address

BkDef.	FDBFID			; define a word for the ID of the fragment.
				; the ID contains:
BkNxt.	FDBPro,hlf.wd		;	in the left half, the message protocol
BkNxt.	FDBID,hlf.wd		;	in the right half, the message ID

BkNxt.	FDBMsk,2*ful.wd		; first word of bit mask.  one
				;  bit for each 8 byte group.

BkNxt.	FDBLen			; total length of message, if known.

BkNxt.	FDBRCt			; number of octoctets actually received.

BkNxt.	FDBMes			; pointer to actual buffer chain.
BkOff.	FDBMOf			; get offset into block for this, too.

BkNxt.	FDBTTL			; time until we discard the fragments
				; (decremented in once a second code)

	FDBTO==↑d60		; timeout after one minute.

BkEnd.	FDBLen
	subttl	IpIn - handle an incoming IP message


entry	IpIn	; only load if IMPSer calls for this routine

IpIn::
	move	t1,IpPnt		; get pointer to buffer space.
	movei	t2,IpLen*4		; load the number of bytes to get.
	stor.	t2,NBHCnt,IPIBHd	; save as byte count, this buffer.
ifn FtChck,<	; checksumming
	setz	p3,			; start the checksumming at zero
>
	pushj	p,GetLed##		; get the leader and checksum it
	  jrst	NoLead			; not enough words in the stream.
	load.	t1,IpVers,IpIBuf	; get the version
	caie	t1,.IPVer		; is it the current version?
	  jrst	BadVer			; no.  forget it.

	; now read in the options and hold for later
	load.	t1,IPIHL,IpIBuf		; get length of header in words
	subi	t1,IPLen		; get words left to be read in leader
	jumple	t1,IPIn0		; no options to read
	lsh	t1,wd2byt		; convert to bytes
	pushj	p,GetMes##		; read in the options
	  jrst	NoLead			; message ended too soon.
	aos	IPOpt##			; saw an option

IPIn0:	movem	t1,IPOptn		; save pointer to options 'til later.
	skipg	t1			; any options?
	  movei	t1,IpIBHd		; no.  point at input buffer
					;  header for last buffer, but
					;  have no first buffer.
	hrrm	t1,ABfLst(f)		; save last buffer of assembled stream.
	hlrz	p1,t1			; get just first buffer number
	stor.	p1,NBHNxt,IpIBHd	; link to IP in case ICMP has
					;  to fire this back out.
ifn FtChck,<	; doing checksumming
	load.	t1,IPHChk,IpIBuf	; get the checksum from the leader
	jumpe	t1,IPNCk		; this guy doesn't do checksums

	; bear in mind that the checksum we now have in P3 has, along with
	;  all the right stuff, its one's complement.  therefore, what
	;  we really have is <checksum> + -<checksum>, which is 0.
	;  further, since <checksum> has some bit on (otherwise the
	;  sender isn't checksuming and we wouldn't be here), it can be
	;  shown that the brand of one's complement 0 we must have is
	;  the version with all 1's.  if that's what we have, we're ok.
	;  if not, the checksum failed.
	hrrzs	p3			; get just the checksum
	caie	p3,<1←↑d16>-1		; magic, as explained above
	  jrst	BadChk			; checksum failed

IPNCk:	; here to skip over the checksum checks because sender is not
	;  checksumming.
>
	subttl	now parse options


	setzb	p3,s			; clear count register and flags
OptnLp:	pushj	p,NxtByt##		; get next option
	  jrst	OptDun			; no more
	hrlzi	t4,-OptCnt		; point at options
TryNxt:	camn	t1,OptNum(t4)		; is this the right option?
	  jrst	@OptDis(t4)		; yes.  jump to the processing routine
	aobjn	t4,TryNxt		; try the next option.

	aos	IPEUOp##		; we don't understand this option.
OptSkp:	pushj	p,OptFls##		; flush the option
	  jrst	OptDun			; all done.
	jrst	OptnLp			; and try the next option

OptDun:
	load.	t1,IpDA,IpIBuf		; get destination address
	came	t1,IPAddr		; is it us?  (option parsing could
					;  have changed it, or it was always
					;  addressed to someone else.)
	  Not2Us==IpFlsh		; implement this later
	  jrst	Not2Us			; no.  do whatever needs doing.
					;  (i.e., retarget and send it.)
	subttl	rebuild a fragmented message


	load.	t1,IpTLen,IpIBuf	; get total length of message
	load.	t2,IpIHL,IpIBuf		; and length of this header
	lsh	t2,wd2byt		; convert from words to bytes
	sub	t1,t2			; get the length of the data
	movem	t1,MsgLen(f)		; save this as the length of
					;  the message.
	load.	t1,IpSA,IpIBuf		; get source address
	load.	t2,IpDA,IpIBuf		; and destination address
	load.	t3,IpID,IpIBuf		; get ID
	load.	t4,IpProt,IpIBuf	; get protocol
	hrl	t3,t4			; put protocol and ID together

	skipn	p1,FstFDB		; get first FDB.
	  jrst	NoFDB			; none there.
FDLook:	cam.	t3,FDBID,(p1),n		; is it this message?
	 cam.	t1,FDBSou,(p1),e	; is this our source?
	  jrst	FDLoop			; no.  try next FDB.
	cam.	t2,FDBDes,(p1),n	; and our destination?
	  jrst	FDBFnd			; this is our FDB
FDLoop:	load.	p1,FDBNxt,(p1)		; get next FDB in chain
	jumpn	p1,FDLook		; and see if that's what we want.

NoFDB:	; no FDB found for this message.
	load.	t4,IpFrgF,IpIBuf	; get the fragmentation field
	jumpe	t4,FrgDun		; if not fragmented, just proceed

	aos	IPFrag##		; count another fragmented message

	pushj	p,AllFDB		; get a free FDB in P1.
	  jrst	IPFlsh			; can't get one.

	stor.	t1,FDBSou,(p1)		; save the source in the FDB
	stor.	t2,FDBDes,(p1)		; save the destination, too.
	stor.	t3,FDBID,(p1)		; save the ID/protocol
FDBAdd:	load.	p2,IPFOff,IpIBuf	; get just the fragment offset
	lsh	p2,Oct2by		; convert to 8 bit bytes
	move	t1,MsgLen(f)		; get the length of this packet
	add	t1,p2			; add on the fragment's offset
	caile	t1,MaxIPS		; is it too large for us?
	  jrst	IpFFDB			; yes.  flush it all.
	trnn	t4,IP%MF		; is this the last fragment?
	  stor.	t1,FDBLen,(p1)		; yes.  store total length in FDB

	; check to see if there are enough buffers allocated
	idivi	p2,NBfByt		; P2 = buffer number.
					; P3 = bytes in buffer before fragment
	movei	t4,FDBMOf(p1)		; point at message buffer area
MakeLp:	pushj	p,NxtOBf		; check next buffer, allocate
					;  if we must
	  jrst	IpFFDB			; not enough buffers.  flush it all.
	sojge	p2,MakeLp		; allocate as many as we need
					;  to start.

	; now transfer bytes from input stream to fragment buffer
	move	p2,p3			; copy of bytes used in this buffπr
	lsh	p2,byt2wd		; change to words used
	addi	p2,NBHLen(t4)		; point to the word to be filled
	hrli	p2,(point 8,)		; and make it a pointer.
	subi	p3,NBfByt		; convert to negative number of
					;  bytes still allowed in buffer.
	hrlm	t4,(p)			; save current target buffer

FillLp:	jsp	p4,(p4)			; get next byte from input stream
	  jrst	FillDn			; nothing left.  all done filling.
	aojle	p3,FillL1		; there's still room here?
	hlrz	t4,(p)			; get back current buffer
	pushj	p,NxtOBf		; get the next buffer
	  jrst	IpFFDB			; no free buffers.  flush fragment.
	hrlm	t4,(p)			; save new current back on the stack.
	move	p2,[point 8,NBHLen(t4)]	; point properly at next byte
	movni	p3,<NBfByt-1>	; reset count
FillL1:	idpb	t1,p2			; dump in next slot
	jrst	FillLp			; and loop until filled


FillDn:	hlrz	t4,(p)			; get T4 back for the last time
	load.	t1,FDBLen,(p1)		; get the length
	jumpe	t1,FillD1		; haven't seen last fragment yet.
	load.	t2,NBHNxt,(t4)		; get a pointer to the next buffer.
	jumpn	t2,FillD1		; this is not the last buffer
	idivi	t1,NBfByt		; compute number of bytes in
					;  last buffer into t2.
	skipn	t2			; zero really means "full"
	  movei	t2,NBfByt		; indicate "full"
	stor.	t2,NBHCnt,(t4)		; save count in buffer header.

FillD1:	; now set all the bits in the mask which we just copied in.
	move	t4,MsgLen(f)		; get the length of this one again
	addi	t4,7			; make sure we round up
	lsh	t4,-Oct2By		; how many groups of 8 bytes?
	load.	t1,IpFOff,IpIBuf	; recall the offset start
	idivi	t1,ful.wd		; which bit in which word of
					;  the map?
	movei	t3,1			; low order bit for first octoctet
	lsh	t3,(t2)			; shift into the correct position
	addi	t1,(p1)			; point into this FDB
	load.	t2,FDBMsk,(t1)		; get the word we're working on
MaskLp:	tdon	t2,t3			; set the bit in the word
	  incr.	,FDBRCt,(p1)		; count this uncounted thingy.
	lsh	t3,1			; next bit
	jumpn	t3,MaskL1		; still bits to set before i die
	stor.	t2,FDBMsk,(t1)		; save this word with bits set
	aos	t1			; move along to next word
	load.	t2,FDBMsk,(t1)		; retrieve the next mask
MaskL1:	sojg	t4,MaskLp		; loop to set all bits
	stor.	t2,FDBMsk,(t1)		; save the last mask we worked on.

	skip.	t1,FDBLen,(p1),n	; do we know how long it is yet?
	  jrst	StlFrg			; no.  can't be done yet.
	addi	t1,7			; round up octoctets
	lsh	t1,-Oct2By		; divide by 8 to get octoctets
	cam.	t1,FDBRCt,(p1),le	; and have we got that many yet?
	  jrst	StlFrg			; no.  not done yet.  just return.

	aos	IPFDun##		; fragmented message fully rebuilt.

	movei	p4,InByte##		; input from buffers which are already
					;  in 32 bit words.
	setzm	IBfBC(f)		; zero byte count: start fresh.
	load.	t1,FDBMes,(p1)		; get the buffer.
	hrrom	t1,IBfThs(f)		; save as current buffer, untouched.
	zero.	t1,FDBMes,(p1)		; now detach it from the FDB.
	load.	t1,FDBLen,(p1)		; get total length of message.
	movem	t1,MsgLen(f)		; copy into DDB
	jrst	FrgOne			; fragment is reassembled, and
					;  we just specified how to
					;  read bytes, so delete the
					;  FDB and continue.

FDBFnd:	load.	t4,IpFrgF,IpIBuf	; get frag field
	jumpn	t4,FDBAdd		; if this is fragmented, add it in.
FrgOne:	pushj	p,FlsFDB		; throw out existing FDB: we just
					;  got the entire message at once.

FrgDun:	; here when we have a completed message to pass on.
	load.	t1,IPSA,IpIBuf		; get the source address
	movem	t1,RmtAdr(f)		; that's where it came from
	load.	t1,IPDA,IpIBuf		; get our address
	movem	t1,LclAdr(f)		; that's the destination
	load.	t1,IPTOS,IpIBuf		; get the type of service required.
	movem	t1,SerTyp(f)		; remember it.
	load.	t1,IPProt,IpIBuf	; get the protocol into t1
	movem	t1,Protcl(f)		; save that correctly for later, too.
	hrlzi	t4,-PrtCnt		; set negative counts of protocols
ProtLp:	camn	t1,PrtNum(t4)		; is this the correct protocol module?
	  jrst	PrtFnd			; found the proper protocol
	aobjn	t4,ProtLp		; loop over all protocols

	aos	IPEPrt##		; count seeing a protocol we
					;  didn't understand.
	setzm	IpOptn			; the options will get flushed when
					;  the ICMP message is built.
	movei	t1,.icDU		; destination unreachable
	movei	t2,.idPlU		; protocol unreachable
	scnoff				; no interrupts allowed
	pushj	p,RedSn0		; send out an ICMP telling him
					;  we don't do that protocol.
	pjrst	sonppj##		; interupts on and go home.


PrtFnd:	pushj	p,@PrtHnd(t4)		; yes. call the protocol
					;  handler for that protocol.
	jrst	IpFlsh			; clear the IP options.
	subttl	returns

; error returns before IP options are read in.
BadVer:	; IP version number was not the one we support
	aosa	IPEVer##		; count version error and skip
NoLead:	; not enough words for a leader
	  aos	IPELed##		; count IP leader error
	popj	p,			; no other clean up needed

; various returns after IP options have been read in.
BadChk:	  aos	IPEChk##		; count checksum error

IPFlsh:	; ending without error
	skipe	t1,IpOptn		; any undeleted options?
	  pushj	p,RelBuf##		; yes, delete them.
	setzm	IpOptn			; make sure options are flushed.
	popj	p,			; nothing smart yet, just a bad return


; here to flush an FDB and leave incoming processing.
IpFFDB:	pushj	p,FlsFDB		; flush the FDB
	jrst	IpFlsh			; now clean up and return.

; here after adding to a fragmented buffer without completing the buffer.
StlFrg:	movei	t1,FDBTO		; get the timeout time
	stor.	t1,FDBTTL,(p1)		; reset it
	jrst	IPFlsh			; and leave
	subttl	Option handlers

; first define dispatch information for all of them.
;  each option has a call on the macro OPT which takes two arguments:
;	1. the option number, in decimal
;	2. the option handling routine.  should not use P1-P4.
define	Options,
    <
	opt	(  0,OptDun)		; end of option list (stop processing)
	opt	(  1,OptnLp)		; no-op (do nothing)
	opt	(130,OptSkp)		; security option (ignore for now)
	opt	(131,OptLSR)		; loose source and record route
	opt	(137,OptSSR)		; strict source and record route
	opt	(  7,OptRec)		; record route
	opt	(136,OptSkp)		; stream identifier (skip over it)
	opt	( 68,OptTim)		; internet time stamp
    >


; now define the table of option numbers
define	opt( number,dispat ),<	↑d'number  > ; just a number for each entry.

OptNum:	Options				; expand options
OptCnt==.-OptNum			; get a count of number of options


; and the dispatch vector
define	opt( number,dispat ),<	z  'dispat  >	; dispatch routine

OptDis:	Options				; expand the options again
	subttl	Option parsing code


OptSSR:			; strict source and record route
	txo	s,IP$Str		; remember we saw strict routing.
	; fall into loose code.
OptLSR:			; loose source and record route
	load.	t1,IPDA,IPIBuf		; get the IP's destination
	came	t1,IPAddr		; is this us?
	  jrst	OptFls			; no.  just skip this option.
	txoa	s,IP$Rou		; remember to route this
					;  message to next host in
					;  source route.
OptRec:	  txz	s,IP$Rou		; just recording route: don't
					;  save a new source we think
					;  we found.
	pushj	p,OptSet		; set up length, etc., in
					;  standard form.
	  jrst	OptDun			; ran out of option space.
	pushj	p,OptPos		; position for our entry
	  jrst	OptnLp			; some end of stream that means
					;  we don't enter an entry.
	move	t1,IPAddr		; get our address
	pushj	p,RplWrd##		; replace this word in the
					;  input stream.
	  ;<not enough bytes.  discard and reply with ICMP>
	  jrst	OptDun			; until implemented
	movei	t2,4			; four bytes more to account for.
	txze	s,IP$Rout		; supposed to be routing?
	  stor.	t1,IpDA,IPIBuf		; yes: store the word read as the
					;  new destination.
	; fall into finishing code

; here with T2 = bytes in rewritten fields, T3 with bytes left in
;	option (INCLUDING rewritten fields) and T4 an LDB pointer
;	to pointer byte.	
OptFin:	ldb	t1,t4			; get the pointer byte
	add	t1,t2			; point past the next address
	dpb	t1,t4			; store that back in place
OptEnd:	move	t1,t3			; get the number of bytes left
					;  to be read in this option.
	sub	t1,t2			; take into account fields we
					;  just wrote over.
	pushj	p,NxtFls##		; skip them
	  jrst	OptDun			; not enough.  all done.
	jrst	OptnLp			; keep parsing.

OptTim:			; internet time stamp
	pushj	p,OptSet		; read length word.
	  jrst	OptDun			; ran out of space.
	pushj	p,NxtByt##		; read in overflow field and flags
	  jrst	OptDun			; end of options hit.  done.
	push	p,p2			; save pointer to overflow
					;  field in case we need it later.
	hrrz	s,t1			; save flags (and overflow field) in S.
	sos	t2			; one less to read to position
	sos	t3			; one less to read in option
	pushj	p,OptPos		; position for the field we're after.
	  jrst	OptOvf			; not enough bytes.  we need to
					;  increment overflow field.
		OptOvf==OptnLp		; don't do anything for now
	pop	p,(p)			; we won't need the overflow field.
	setz	t2,			; no bytes rewritten so far.
	txnn	s,IP%Adr		; flags say this has internet
					;  addresses with times?
	  jrst	NoAddr			; no.  skip address handling
	addi	t2,4			; four bytes are going to be passed
	txnn	s,IP%Set		; addresses are preset?
	  jrst	ChkAdr			; no.  check address, don't
					;  rewrite it.
	move	t1,IPAddr		; get our address
	pushj	p,RplWrd##		; put that in place, get the word read					;  value read.
	  ;<error.  discard and answer with ICMP>
	  jrst	OptDun			; until implemented
	jrst	NoAddr			; rejoin checking code

ChkAdr:	pushj	p,NxtWrd##		; read the internet address
					;  from stream.
	  ;<error>
	  jrst	OptDun			; until implemented
	came	t1,IPAddr		; is it our address?
	  jrst	OptEnd			; no.  we do nothing for this option

NoAddr:	pushj	p,MilTim##		; get time since midnight in
					;  milliseconds.
	pushj	p,RplWrd##		; put that in place
	  ;<error.  discard and respond with ICMP>
	  jrst	OptDun			; until implemented
	addi	t2,4			; four more bytes rewritten
	jrst	OptFin			; update pointer byte and go
					;  for next option.
	subttl	subroutines to help option parsing	  

; set up routine for reading a standard option type.
; returns with
;	T2 = number of bytes to read from here to get to field
;		"pointer" points to.
;	T3 = number of bytes left to be read in this option
;	T4 = LDB pointer to pointer field, in case we need to INCR it later.
OptSet:
	pushj	p,NxtByt##		; read the length
	  popj	p,			; not there.  all done.
	movei	t3,-3(t1)		; save length in "safe" place,
					;  accounting for type, length
					;  and pointer bytes.
	pushj	p,NxtByt##		; get pointer
	  popj	p,			; can't.  give up.
	move	t4,p2			; save LDP pointer to pointer value.
	movei	t2,-4(t1)		; make this the number of bytes
					;  to read to get to our place.
	pjrst	cpopj1##		; skip return.

; position ourselves to read the field we are being pointed at.
; call with P1-P3 and T2-T4 set up as they are on return from OptSet.
OptPos:
	camg	t3,t2			; does it point past end of the
					;  option?
	  jrst	DntFil			; yes.  can't fill anything in
	sub	t3,t2			; figure how much will be left after
					;  we read up to our position.
	move	t1,t2			; position number of bytes to read.
	pjrst	NxtFls##		; skip up to the beginning of
					;  the field we're pointed at
					;  and return.

DntFil:	move	t1,t3			; number of bytes 'til end of option.
	pushj	p,NxtFls##		; flush that many bytes
	  popj	p,			; not enough bytes
	popj	p,			; enough bytes, but we still
					;  don't have room.
	subttl	protocol definitions


; define all the protocols we're prepared to handle.
; each definition is a call on the macro Prot with two
; arguments: the protocol number (in decimal) and the routine to call
; when a message of this protocol comes in.
define	Prots,
    <
	prot	( 1,ICMPIn )		; handle ICMP messages
	prot	( 6,TCPIn## )		; handle TCP messages
;	prot	(17,UDPIn## )		; handle UDP
    >

; now define the table of protocol numbers
define	prot(number,routine),<	↑d'number  >	; just put down the number
PrtNum:	Prots
PrtCnt==.-PrtNum			; get the count of protocols.

; define the dispatch table
define	prot(number,routine),<	z 'routine >	; define the routine
PrtHnd:	Prots
	subttl	AllFDB

;++
; Functional description:
;
;	allocate an FDB from IMP buffers
;
;
; Calling sequence:
;
;		pushj	p,AllFDB
;		  <return here if no buffer available>
;		<return here with buffer in P1 and linked into FDB chain>
;
; Input parameters:
;
;	none.
;
; Output parameters:
;
;	P1 - new buffer.
;
; Implicit inputs:
;
;	FDB chain values.
;
; Implicit outputs:
;
;	FDB block entries.
;
; Routine value:
;
;	returns non-skip if there is no buffer to be had.
;
; Side effects:
;
;	allocates a buffer.
;--

AllFDB:	pushj	p,savt##		; get all T registers
	pushj	p,BufGet##		; get a fresh buffer.
	  popj	p,			; ACK!  can't get one.
	move	p1,t1			; position results for return
	move	t1,FstFDB		; get the first FDB in chain.
	stor.	t1,FDBNxt,(p1)		; link the old first to this one.
	movem	p1,FstFDB		; and make us the first.
	stor.	p1,FDBLst,(t1)		; make us the previous for the next.
	movei	t1,FDBTO		; get time out value
	stor.	t1,FDBTTL,(p1)		; and save that, too.
	jrst	cpopj1##		; and give a good return.
	subttl	FlsFDB

;++
; Functional description:
;
;	return the given FDB and all buffers linked to this FDB to the
;	free buffer chain.
;
;
; Calling sequence:
;
;		move	p1,FDB
;		pushj	p,FlsFDB
;		<always returns here>
;
; Input parameters:
;
;	P1 - points to FDB to be discarded.
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	data in FDB.
;
; Implicit outputs:
;
;	FDB chain.
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	discard FDB and all attached buffers.  closes up FDB chain.
;--

FlsFDB:	load.	t1,FDBMes,(p1)		; get buffers
	pushj	p,RelBuf##		; discard entire chain of buffers.
	load.	t1,FDBNxt,(p1)		; get next FDB in chain.
	load.	t2,FDBLst,(p1)		; get previous FDB in chain
	jumpe	t2,[			; is there a FDB before to me?
		 movem	t1,FstFDB	; no.  the next is now first.
		 jrst	FlsFD1		; continue.
		]
	stor.	t1,FDBNxt,(t2)		; follower now follows predecessor.
FlsFD1:	skipe	t1			; if there is a next buffer....
	  stor.	t2,FDBLst,(t1)		; ...its "last" should be updated.
	move	t1,p1			; point at this FDB
	pjrst	BufRel##		; release it.
	subttl	IpSec

;++
; Functional description:
;
;	once a second code for IP.  it checks for time outs in
;	the fragmentation reassembly chain.
;
;
; Calling sequence:
;
;		move	t1,<0 to do timeout check, -1 to flush all>
;		scnoff
;		pushj	p,IpSec		; once a second
;		<always return here>
;
; Input parameters:
;
;	t1 - if 0, IPSEC will decr the timeout field of the FDB and
;		delete any FDBs that have been around too long.  if
;		t1 is -1, all FDBs are flushed.
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	FstFdb and the fragmentation chain.
;
; Implicit outputs:
;
;	none.
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	make delete fragment blocks from the fragment chain.
;--


IPSec::	pushj	p,save2##		; get two register
	move	p2,t1			; save arg in one of them
	move	p1,FstFdb		; get first FDB in chain
IpSec1:	jumpe	p1,CPOPJ##		; return if end of chain (turn
					;  on interrupts).
	jumpl	p2,IpSec3		; always delete if flushing
	decr.	,FDBTTL,(p1),le		; count one more second.  expired?
	  jrst	IpSec4			; no.  don't flush
	load.	t1,FDBMsk,(p1)		; get first word of mask
	trnn	t1,1			; first 8 bytes in yet?
	  jrst	IpSec3			; no.  don't send an ICMP error
					;  because we haven't got the
					;  next level's leader.
	skipa	t2,[IpIBHd]		; point at input leader
IpSecL:	  move	t2,t1			; remember this buffer
	load.	t1,NBHNxt,(t2)		; get next buffer
	jumpn	t1,IpSecL		; loop until done.
	push	p,f			; protect F
	setzm	IpPDDB+PDBTop		; zero first word of pseudo-DDB
	move	f,[ xwd	IpPDDB+PDBTop,IpPDDB+PDBTop+1 ]	; BLT pointer
	blt	f,IpPDDB+PDBBot		; clear entire DDB
	movei	f,IpPDDB		; point at hypothetical start
	load.	t1,FDBSou,(p1)		; get the source of this fragment
	movem	t1,RmtAdr(f)		; that's where we're sending the error
	pushj	p,Target##		; try to get an arpa address.
	  jrst	IpSec2			; can't get there.  just flush
	movem	t1,NetAdr(f)		; save that for ARPA handler.
	move	t1,IpAddr		; get our address
	movem	t1,LclAdr(f)		; we're the one sending this
	setzm	SndNxt(f)		; clear these two entries so
	setzm	SndLst(f)		;  this message isn't retransmitted.
	load.	t1,FDBMes,(p1)		; get message pointer
	stor.	t1,NBHNxt,(t2)		; link this to the current
					;  input leader.
	zero.	t1,FDBMes,(p1)		; don't let FlsFDB delete message.
	pushj	p,FlsFDB		; ditch the FDB now
	movei	t1,.icTEx		; time exceeded
	movei	t2,.itFRT		; fragment reassembly time,
					;  that is.
	pushj	p,CutSn0		; cut down message and send it
	jrst	IpSec4			; and loop

IpSec2:	pop	p,f			; restore F
IpSec3:	pushj	p,FlsFDB		; yes.  flush this FDB
IpSec4:	load.	p1,FDBNxt,(p1)		; get the next one
	jrst	IpSec1			; and loop
	subttl	NxtOBf

;++
; Functional description:
;
;	get the next buffer for output.  if none allocated, allocate
;	one and link it to the last.
;
;
; Calling sequence:
;
;		move	t4,<pointer to current buffer header>
;		pushj	p,NxtOBf
;		  <return here if a buffer was needed, but none available>
;		<return here with T4 containing pointer to next buffer>
;
; Input parameters:
;
;	T4 - pointer to current buffer header.  this can point to the
;		FDBMes word.
;
; Output parameters:
;
;	T4 - pointer to next buffer
;
; Implicit inputs:
;
;	buffer stream.
;
; Implicit outputs:
;
;	buffer stream.
;
; Routine value:
;
;	returns non-skip if it needed to allocate a buffer, but there
;	were none available.
;
; Side effects:
;
;	may link another buffer on to the stream.
;	clobbers T2 and T3.
;--

NxtObf:	push	p,t1			; get a scratch

	load.	t1,NBHNxt,(t4)		; get next buffer pointer
	jumpn	t1,NxtOB1		; there's one there, so go to it
	pushj	p,BufGet##		; allocate a buffer
	  pjrst	tpopj##			; none available.  take error return.
	stor.	t1,NBHNxt,(t4)		; link up to last buffer
	movei	t2,NbfByt		; make the previous buffer full
	stor.	t2,NBHCnt,(t4)		; ...
NxtOB1:	move	t4,t1			; position for return
	pjrst	tpopj1##		; good return
	subttl	IPMake

;++
; Functional description:
;
;	get a fresh buffer and put an IP leader (in 32 bit format)
;	into it.  then link the buffer to the beginning of the
;	current output stream.  then send this message down to
;	1822 level (IMPSER) to get it fired off.
;
;
; Calling sequence:
;
;		move	f,DDB
;		pushj	p,IpMake
;		<always returns here>
;
; Input parameters:
;
;	f - DDB for connection
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	data in DDB
;
; Implicit outputs:
;
;	data in DDB
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	adds a buffer to the beginning of the current output stream.
;--


IpMake::
	setzm	IPOBuf+NBHLen		; clear first word of leader
	move	t1,[IPOBuf+NBHLen,,IPOBuf+NBHLen+1]	; set up blt pointer
	blt	t1,IPOBuf+IPLen+NBHLen-1; clear out leader.

	movei	t1,IPOBuf		; point at this header
	exch	t1,OBfFst(f)		; make us first and get old first.
	stor.	t1,NBHNxt,IpOBuf	; link old first to us.
	movei	t1,.IpVer		; load the version up
	stor.	t1,IpVers,NBHLen+IpOBuf	; store that in place
	movei	t2,IPLen		; get length (will need to compute
					;  this when we perform options.)
	stor.	t2,IPIHL,NBHLen+IpOBuf	; save that.
	lsh	t2,Wd2Byt		; convert from words to bytes
	stor.	t2,NBHCnt,IpOBuf	; save byte count for this buffer
	move	t1,t2			; put in T1 (save T2 for checksumming)
	addb	t1,OBfByt(f)		; get a grand total in bytes.
	stor.	t1,IPTLen,NBHLen+IpOBuf	; save that in place
	pushj	p,GetID			; choose an ID for this message
	stor.	t1,IPID,NBHLen+IpOBuf	; and put it in the leader
	movei	t1,.IpTTL		; get the standard time to live
	stor.	t1,IpTTL,NBHLen+IpOBuf	; save that
	move	t1,Protcl(f)		; get next level protocol from DDB
	stor.	t1,IPProt,NBHLen+IpOBuf	; and save it
	move	t1,LclAdr(f)		; get our address
	stor.	t1,IPSA,NBHLen+IpOBuf	; save it.
	move	t1,RmtAdr(f)		; get his address
	stor.	t1,IPDA,NBHLen+IpOBuf	; and save it, too.

	; one would add OPTIONS around here somewhere.

	setz	p3,			; clear checksum word
ifn FtChck,<	; doing checksums?
	move	t1,[point 16,NBHLen+IpOBuf]; point at the leader
	; length in bytes is already in T2
	pushj	p,CSmWds##		; checksum them all
	txc	p3,msk.hw		; send one's complement of the sum
	txnn	p3,msk.hw		; if zero, make it...
	  movei	p3,msk.hw		; ...the zero with all bits on
>
	stor.	p3,IpHChk,NBHLen+IpOBuf	; save it.

	movei	t1,.lnkip		; get our "link" number to tell
					;  1822 level that that's who
					;  we are.
	pjrst	ImpMak##		; and call IMP level processing
					;  to send it off.
	subttl	GetID

;++
; Functional description:
;
;	get an ID number for some outgoing IP message
;
;
; Calling sequence:
;
;		pushj	p,GetID
;		<always returns here, ID in T1>
;
; Input parameters:
;
;	none.
;
; Output parameters:
;
;	T1 - an ID
;
; Implicit inputs:
;
;	none.
;
; Implicit outputs:
;
;	none.
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	updates ID number so we don't get the same number twice.
;--


GetId:	aos	t1,LstID		; incr it and retrieve it.
	popj	p,			; and return
	subttl	ICMP message handling code


ICMPln==2		; number of words in an ICMP leader.
ICMNLL==↑d8		; number of bytes of the next level's leader with
			;  an ICMP reply message

	$low		; define the storage needed

ICMPIB:	block	ICMPLn			; words needed for header

; the following block is used and removed under ScnOff.
ICMPOB:	xwd	ICMPLn*4,0		; pseudo leader in case needed
	block	ICMPLn			; buffer for forming ICMP leaders
					;  for output.

NxtLBf:	xwd	ICMNLL,0		; number of bytes here.
NxtLvl:	block	ICMNLL/4		; space for 64 bit of leader
					;  for the next level protocol
					;  if needed for ICMP message.

	$high		; back to protected code

ICMPPn:	point	8,ICMPIB		; pointer to start loading the
					;  header block from the stream.
NxtLPn:	point	8,NxtLvl		; pointer to storage area for
					;  "next level" leader.

; define the actual header fields.  position is the bit position of the
;  left most bit.
;
; 	name   word  position width
DefFd.	ICMPTp,	0,	0,	8	; type of message
DefFd.	ICMPCd,	0,	8,	8	; code field
DefFd.	ICMPCs,	0,	16,	16	; checksum
DefFd.	ICMP2d,	1,	0,	36	; second word, in case it
					;  should be zeroed.
DefFd.	ICMPPt,	1,	0,	8	; pointer (parameter problem message)
DefFd.	ICMPGA,	1,	0,	32	; gateway address (redirect message)
DefFd.	ICMPID,	1,	0,	16	; identifier (echo, time, info)
DefFd.	ICMPSq,	1,	16,	16	; sequence (echo, time, info)

; words in the data part of the leader.
DefFd.	ICMPOT,	0,	0,	32	; originate timestamp (time)
DefFd.	ICMPRT,	1,	0,	32	; receive timestamp (time)
DefFd.	ICMPTT,	2,	0,	32	; transmit timestamp (time)


; types of ICMP messages
	.icEcR==↑d0		; echo reply
	.icDU==↑d3		; destination unreachable
		.idNU==↑d0	; network unreachable
		.idHU==↑d1	; host unreachable
		.idPlU==↑d2	; protocol unreachable
		.idPoU==↑d3	; port unreachable
		.idFNC==↑d4	; fragments needed but can't fragment (DF set)
		.idSRF==↑d5	; source route failed.
	.icSQ==↑d4		; source quench
	.icRed==↑d5		; redirect
	.icEch==↑d8		; echo
	.icTEx==↑d11		; time exceeded
		.itTTL==↑d0	; time to live expired
		.itFRT==↑d1	; fragment reassembly time expired
	.icPrm==↑d12		; parameter problem
	.icTim==↑d13		; timestamp
	.icTSR==↑d14		; timestamp reply
	.icInf==↑d15		; information request
	.icInR==↑d16		; information reply
	subttl	ICMPIn - handle an incoming ICMP message


ICMPIn:
	pushj	p,MilTim##		; get time (in case timestamp)
	movem	t1,RcvTim		; save it
ifn FtChck,<	; checksumming
	setz	p3,			; start the checksumming at zero
>
	move	t1,ICMPPn		; get pointer to buffer space.
	movei	t2,ICMPLn*4		; load the number of bytes to get.
	pushj	p,GetLed##		; get the leader and checksum it
	  jrst	NoICLd			; not enough data for the IP leader.

	setz	p1,			; remember the lack of a data buffer.
	move	t1,MsgLen(f)		; get total IP length
	subi	t1,ICMPLn*4		; subtract leaders info
	jumple	t1,NoData		; skip this if nothing to read
	pushj	p,GetMes##		; read in data
	  jrst	WrgLen			; not enough data.  wrong length
	move	p1,t1			; remember the buffer stream
NoData:	
ifn FtChck,<	; doing checksumming
	load.	t1,ICMPCS,ICMPIB	; get the checksum from the leader
	jumpe	t1,ICMPNC		; this guy doesn't do checksums

	; bear in mind that the checksum we now have in P3 has, along with
	;  all the right stuff, its one's complement.  therefore, what
	;  we really have is <checksum> + -<checksum>, which is 0.
	;  further, since <checksum> has some bit on (otherwise the
	;  sender isn't checksuming and we wouldn't be here), it can be
	;  shown that the brand of one's complement 0 we must have is
	;  the version with all 1's.  if that's what we have, we're ok.
	;  if not, the checksum failed.
	hrrzs	p3			; get just the checksum
	caie	p3,<1←↑d16>-1		; magic, as explained above
	  jrst	WrgChk			; checksum failed

ICMPNC:	; here to skip over the checksum checks because sender is not
	;  checksumming.
>

	load.	t1,ICMPTp,ICMPIB	; get type
	cail	t1,ICMCnt		; is it in the range we know?
	  jrst	TypUnk			; no.  count error, etc.
	aos	ICMTyp##(t1)		; count this ICMP message type
					;  for GETTABs.
	jrst	@ICMDis(t1)		; yes.  dispatch to it.
	subttl	ICMP returns


NoICLd:	aosa	ICMNLd##		; not enough to get a leader
WrgLen:	  aos	ICMDEr##		; IP length was shorter than
					;  data read in.
	popj	p,			; and return

WrgChk:	aosa	ICMChk##		; wrong checksum.  count it.
TypUnk:	  aos	ICMUnT##		; count unknown type
RelDat:	hlrz	t1,p1			; get pointer at data
	pjrst	RelBuf##		; release buffers
	subttl	ICMP message type definitions


; dispatch vector for ICMP message types
ICMDis:
	ICMEcR		;(0) echo reply
	ICMUDf		;(1) undefined
	ICMUDf		;(2) undefined
	ICMCGT		;(3) destination unreachable (can't get there).
	ICMSQ		;(4) source quench
	ICMRed		;(5) redirect message
	ICMUDf		;(6) undefined
	ICMUDf		;(7) undefined
	ICMEch		;(8) echo
	ICMUDf		;(9) undefined
	ICMUDf		;(10) undefined
	ICMTEx		;(11) time exceeded
	ICMPrm		;(12) parameter problems
	ICMTim		;(13) timestamp
	ICMTSR		;(14) timestamp reply
	ICMInf		;(15) information request
	ICMInR		;(16) information reply

ICMCnt==.-ICMDis	; number of types supported

ifg ICMCnt - ICMLen,<	; make sure NetSub has room to remember
			;	 the number of message types we may
			;	 have.
printx ? ICMLen (from NetDef.MAC) must be greater than or equal to ICMCnt
>

ICMUDf==RelDat		; do nothing for undefined messages
ICMEcR==RelDat		; don't send echo messages, so can't get a reply
ICMTSR==RelDat		; don't send timestamps, so ignore replies.
ICMInR==RelDat		; don't send info request messages, so can't get
			;  replies to them.
	subttl	handlers for different ICMP message types


; destination unreachable.  update our tables
ICMCGT:
	pushj	p,ICMDDB		; get the DDB that fired off
					;  this message.
	  jrst	RelDat			; i don't think i sent this mesage.
	load.	t1,ICMPCd,ICMPIB	; get the code that tells us
					;  what is is we can't reach.
	txo	t1,1b0			; set sign bit to indicate failure
	movem	t1,State(f)		; and put in the state word
					;  (this makes the "state" change).
	pushj	p,ImpWak##		; wake up user so he notices.
	jrst	RelDat			; and return buffer and self

; source quench.  try to cut down data rate.
ICMSQ:	; just ignore until we think of something clever to do.
	jrst	RelDat			; and return

; redirect.  update all our DDBs that talk to this host so that NetAdr
;		is as indicated.
ICMRed:
	pushj	p,ICMDDB		; get the DDB that sent this
	  jrst	ICMRe1			; not a DDB.  do what we can
	load.	t1,ICMPGA,ICMPIB	; get the internet gateway address.
	txz	t1,NetMsk		; this guy claims it's on our network
	movem	t1,NetAdr(f)		; so use this.
	pushj	p,FixRTQ##		; correct anything we can in
					;  the retransmission queue.

ICMRe1:	; should update targetting tables
	jrst	RelDat			; and return

; echo.  turn into an echo reply
ICMEch:
	move	t1,p1			; point at data
	movei	t2,.icEcR		; type is echo reply
	setz	t3,			; code is 0

; here to copy the ID over and call ICMPMk.  clobbers P3.
ICMCID:	load.	p3,ICMP2d,ICMPIB	; get ID, etc., from incoming
	scnoff				; no interrupts allowed here
	stor.	p3,ICMP2d,ICMPOB+NBHLen	; and put it in outgoing.
	pushj	p,ICMPMk		; try to send it and return.
	pjrst	sonppj##		; interrupts on again and go

; time exceeded.  probably can't do anything
ICMTEx:
	jrst	RelDat			; and return

; parameter problem.  try to save info for later analysys.
ICMPrm:
	jrst	RelDat			; and return

; time stamp.  create reply.
ICMTim:
	move	t1,RcvTim		; get time this packet was received
	stor.	t1,ICMPRT,NBHLen(p1)	; save in buffer
	pushj	p,MilTim##		; get millisecond time since midnight
	stor.	t1,ICMPTT,NBHLen(p1)	; save that as last time we saw it
	move	t1,p1			; point at buffer
	movei	t2,.icTSR		; timestamp reply message
	setz	t3,			; code 0
	jrst	ICMCID			; set ID word and send it

; information request.  supply answer
ICMInf:
	move	t1,RmtAdr(f)		; get address of incoming
	txnn	t1,NetMsk		; is there a network?
	  txo	t1,ArpAdr		; no.  set it
	movem	t1,RmtAdr(f)		; put back fully specified host.
	move	t1,IPAddr		; get my own site number
	movem	t1,LclAdr(f)		; make that our address
	setzb	t1,t3			; no data to send, code 0
	movei	t2,.icInR		; type is info reply.
	pushj	p,ICMCID		; copy ID and send message
	jrst	RelDat			; return any errorneous data.
	subttl	ICMDDB

;++
; Functional description:
;
;	look at an incoming ICMP message with a IP/TCP leader
;	following it and track down the DDB this is aimed at.
;
;
; Calling sequence:
;
;		move	P1,<first buffer of IP/TCP leader>
;		pushj	p,ICMDDB
;		  <returns here is no such DDB exists>
;		<here with DDB pointed to by F>
;
; Input parameters:
;
;	P1 - pointer the the first buffer of the IP and TCP headers.
;
; Output parameters:
;
;	F - pointer to DDB this message applies to.
;
; Implicit inputs:
;
;	none.
;
; Implicit outputs:
;
;	none.
;
; Routine value:
;
;	returns non-skip if no such DDB exists.
;
; Side effects:
;
;	none.
;--


ICMDDB:	pushj	p,save1##		; make sure P1 comes through ok.
	load.	t1,IPDA,NBHLen(p1)	; get the address the message
					;  was being sent to.
	load.	t4,IPProt,NBHLen(p1)	; and get the protocol being used.
	load.	t2,IpIHL,NBHLen(p1)	; get the length of the IP leader.
	addi	t2,StdPrt		; add on the offset to the port word.
					; (this is zero, but might as
					;  well be complete.)
	lsh	t2,wd2byt		; convert to bytes
ICMDD1:	load.	t3,NBHCnt,(p1)		; get the byte count for this buffer
	camge	t2,t3			; is there that much in this buffer?
	  jrst	ICMDD2			; yes.  the TCP leader starts
					;  in this buffer.
	sub	t2,t3			; remove that amount from the count.
	load.	p1,NBHNxt,(p1)		; link on to next buffer
	jumpn	p1,ICMDD1		; loop if another buffer
	popj	p,			; else we couldn't find the DDB
					;  because there's an error in
					;  the ICMP message.  clear
					;  stack and return.

ICMDD2:	lsh	t2,byt2wd		; covert back to word count
	addi	p1,NBHLen(t2)		; point at port word of next level's
					;  leader.
	load.	t2,StdDP,(p1)		; get the destination port.
	load.	t3,StdSP,(p1)		; and the port it was send from
					;  (our local port).
	pjrst	FndDDB##		; go try to find that DDB.
	subttl	ICMPMk

;++
; Functional description:
;
;	prepare a ICMP message and call IP to send it.
;
;
; Calling sequence:
;
;		move	f,DDB
;		move	t1,<message data, or zero if no additional data>
;		move	t2,<ICMP message type>
;		move	t3,<ICMP message code>
;		ScnOff
;		pushj	p,ICMPMk	; second word of ICMP leader
;					;  (in ICMPOB) already setup
;		<akways returns here>
;
; Input parameters:
;
;	f - DDB for connection, usually a pseudo DDB since this is usually
;		in response to an arrivign IP message.
;	t1 - pointer to first buffer in the data to be sent, or zero if
;		no addtitional data is to be sent.
;	t2 - ICMP message type for message
;	t3 - ICMP code for message
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	data in DDB
;
; Implicit outputs:
;
;	data in DDB
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	none.
;--


ICMPMk:
	stor.	t2,ICMPTp,ICMPOB+NBHLen	; store the type
	stor.	t3,ICMPCd,ICMPOB+NBHLen	; and the code
	stor.	t1,NBHNxt,ICMPOB	; link next buffer
	movei	t3,ICMPOB		; point at this leader space
	movem	t3,OBfFst(f)		; make this the first buffer.

	setzb	p3,OBfByt(f)		; clear checksum and byte count
	stor.	p3,ICMPCS,ICMPOB+NBHLen	; set checksum to zero in order
					;  to checksum (or to indicate
					;  that we aren't checksumming).

ifn FtChck,<	; if we're checksumming
ICMPCS:	load.	t2,NBHCnt,(t3)		; get the byte counts in this buffer
	addm	t2,OBfByt(f)		; increase byte count enough
	move	t1,[point 16,NBHLen(t3)]	; and point to data
	pushj	p,CSmWds##		; checksum this buffer
	load.	t3,NBHNxt,(t3)		; get next buffer in chain
	jumpn	t3,ICMPCS		; another buffer to do.

	txc	p3,msk.hw		; send one's complement of the sum
	txnn	p3,msk.hw		; if zero, make it...
	  movei	p3,msk.hw		; ...the zero with all bits on
	stor.	p3,ICMPCS,ICMPOB+NBHLen	; set checksum correctly.
> ; end of IFN FtChchk

	pushj	p,OutPre##		; enough buffer space for message?
	  jrst	ICMPFl			; no.  forget it.
	push	p,Protcl(f)		; save protocol (can't think of
					;  an instance where this will
					;  be important, but be careful.)
	movei	t1,.ipicm		; load ICMP's protocol
	movem	t1,Protcl(f)		; make this the protocol
	pushj	p,IpMake		; link up and send
	pop	p,Protcl(f)		; restore old protocol
	popj	p,			; return to caller

ICMPFl:	pop	p,Protcl(f)		; restore old protocol
	setz	t1,			; clear first buffer pointer
	exch	t1,OBfFst(f)		; get/clear prepared stream
	pushj	p,RelBuf##		; flush it
	popj	p,			; go.
	subttl	RedSnd

;++
; Functional description:
;
;	read in two words (first 64 bits of next level leader, if any),
;	link to IP leader and send out an ICMP message with this as data.
;
;
; Calling sequence:
;
;		move	f,DDB
;		move	t1,<ICMP type>
;		move	t2,<ICMP code>
;		move	p4,<input byte getter>
;		pushj	p,RedSnd	; 2nd word of ICMP message already set
;			or
;		pusgj	p,RedSn0	; 2nd word of ICMP should be 0
;		<always returns here>
;
; Input parameters:
;
;	F - DDB
;	T1 - type for ICMP message
;	T2 - code for ICMP message
;	P4 - coroutine for getting bytes from input stream.
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	DDB, IPIBuf.
;
; Implicit outputs:
;
;	none.
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	tries to put an ICMP message on the output stream.
;	read bytes from P4 bytes stream, possibly discarding buffer.
;	clobbers P3.
;
;--

RedSn0:	zero.	,ICMP2d,ICMPOB+NBHLen	; zero 2nd word of ICMP leader.
RedSnd:
	push	p,t1			; save 1st arg
	push	p,t2			; save 2nd arg
	movei	t1,ICMNLL		; length we need
	move	t2,NxtLPn		; pointer to storage for it
	pushj	p,GetLed##		; get the leader
	  jrst	SndRs1			; not enough bytes.  make sure
					;  not to link up.
	skipa	t2,[IPIBHd]		; point at IP input ledaer buffer.
RedSnL:	  move	t2,t1			; save this buffer pointer
	load.	t1,NBHNxt,(t2)		; get next buffer
	jumpn	t1,RedSnL		; loop until no next buffer

	movei	t1,NxtLBf		; point at buffer for next
					;  level leader.
	stor.	t1,NBHNxt,(t2)		; link it up to this buffer

	jrst	SndRs1			; go get args back from stack
					;  and send message off.
	subttl	CutSnd

;++
; Functional description:
;
;	send an error response to some IP message with next level leader
;	and message already read in.  follows buffers
;	connected to IP input leader to find next level protocol,
;	cuts next level (TCP only at this writing) to only first
;	64 bits of leader, and sends an ICMP reply with these leaders
;	as data.
;
;
; Calling sequence:
;
;		move	f,DDB
;		move	t1,<ICMP message type>
;		move	t2,<ICMP message code>
;		pushj	p,CutSnd	; second word of ICMP leader
;					;  (in ICMPOB) already setup
;		<always returns here>
;
; Input parameters:
;
;	f - DDB
;	T1 - type for ICMP message
;	T2 - code for ICMP message
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	2nd word of ICMP blocks should be set as desired.
;
; Implicit outputs:
;
;	none
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	puts an ICMP message on the output queue.
;	clobbers T1, T2, T3 and T4.
;--


CutSn0:	zero.	,ICMP2d,ICMPOB+NBHLen	; clear second word of ICMP message
CutSnd:	push	p,t1			; preserve arg
	push	p,t2			; and arg
	load.	t1,IPIHL,IPIBuf		; get length of leader
	lsh	t1,wd2byt		; convert to bytes
	addi	t1,ICMNLL		; add in the amount we're supposed
					;  to provide from the next level.
	movei	t2,IPIBHd		; point at input stream
	pushj	p,SkpByt##		; skip those bytes
	jumpe	t2,SndRs1		; not enough for next level leader
	stor.	t1,NBHCnt,(t2)		; set the count to the amount we
					;  needed from this buffer.
	load.	t1,NBHNxt,(t2)		; get next buffer in stream
	zero.	,NBHNxt,(t2)		; clear that pointer
	pushj	p,RelBuf##		; discard the rest of the stream.

; here to point at IP leader and get args back from stack.  called also
;	from RedSnd.
SndRs1:	movei	t1,IPIBHd		; point at beginning of data
	pop	p,t3			; get 2nd arg back (was in T2)
	pop	p,t2			; get 1st arg back (was in T1)
	pjrst	ICMPMk			; send it and return
	subttl	SndNSP

;++
; Functional description:
;
;	send a message to the sender of the current message saying
;	"no such port".
;
;
; Calling sequence:
;
;		move	f,<ddb>
;		pushj	p,SndNSP
;
; Input parameters:
;
;	F - DDB.
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	DDB.  IPIBuf.
;
; Implicit outputs:
;
;	none.
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	tries to put a no such port ICMP message on the output queue.
;--


SndNSP::
	setzm	IpOptn			; we may flush incoming IP options
					;  when we build ICMP message, so
					;  make sure to clear the pointer.
	movei	t1,.icDU		; destination unreachable type
	movei	t2,.idPoU		; port unreachable code
	pjrst	CutSn0			; fire off the response.
	subttl	data storage area

	$low

FstFdb:	0		; first FDB in system chain.
LstId:	0		; last IP ID given.
RcvTim:	0		; time the latest ICMP message was received.
IpOptn:	0		; incoming IP options point if option buffers
			;  have not yet been deleted.

IpPDDB=.-PDBTop		; define hypothetical start of our pseudo DDB
	block	PDBBot-PDBTop+1	; number of words we really use

	$high
	$lit
	end